/*
 *  Copyright (C) 2010 Glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.transaction;

import org.balisunrise.annotation.FilterType;
import org.balisunrise.application.Application;
import org.balisunrise.builder.FactoryException;
import org.balisunrise.common.Criterion;
import org.balisunrise.common.BindingGroup;
import org.balisunrise.common.FieldComponent;
import org.balisunrise.common.FieldInput;
import org.balisunrise.components.ComponentConverterException;
import org.balisunrise.components.ComponentManager;

/**
 *
 * @author Glauco
 */
public class CriterionablePanelField extends PanelField
        implements org.balisunrise.common.CriterionablePanelField{

    public static final String PROP_VALUE = "value";
    public static final String PROP_FIELD_PRE_NAME = "fieldPreName";
    public static final String PROP_FILTER_TYPE = "filterType";

    /**
     * Valor para realizar a pesquisa.
     */
    private Object value;

    /**
     * Nome predecessor do nome do campo para filtrar.
     */
    private String fieldPreName;

    /**
     * Tipo do filtro para seleção.
     */
    private FilterType filterType;

    /**
     * Criterion para seleção.
     */
    private Criterion criterion;

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        Object oldValue = this.value;
        this.value = value;
        changeSupport.firePropertyChange(PROP_VALUE, oldValue, value);

        //if (oldValue == null || value == null || !oldValue.equals(value))
            try{
                
                FieldInput field = getFieldInput();
                if (field == null)
                    return ;

                ComponentManager cm = field.getComponentManager();
                if(cm == null)
                    return ;

                cm.insertValue(value, field.getVisualRepresentation());

            }catch(ComponentConverterException ccex){
                System.out.println(ccex.getMessage());
            }
    }
    
    public String getFieldPreName() {
        return fieldPreName;
    }

    public void setFieldPreName(String fieldPreName) {
        String old = this.fieldPreName;
        this.fieldPreName = fieldPreName;
        changeSupport.firePropertyChange(
                PROP_FIELD_PRE_NAME, old, fieldPreName);
    }

    public FilterType getFilterType() {
        return filterType;
    }

    /**
     * Adiciona um nome predecessor ao nome do field.
     * É utilizado para filtrar atravéz de campos encapsulados por entidades
     * de entidades.
     * Por Exemplo:
     * A Classe Estado tem uma referencia a Pais, então para filtrar os
     * estados pelo nome do pais usariamos "pais.nome" onde "nome" seria o
     * campo e "pais" seria o pré-name.
     *
     * @param prefix
     */
    public void addPrefix(String prefix) {
        if (fieldPreName == null || fieldPreName.isEmpty())
            setFieldPreName(prefix);
        else
            setFieldPreName(prefix + "." + fieldPreName);
    }
    
    public void setFilterType(FilterType filterType) {
        FilterType oldFilterType = this.filterType;
        this.filterType = filterType;
        changeSupport.firePropertyChange(
                PROP_FIELD_PRE_NAME, oldFilterType, filterType);
    }

    @Override
    public void bindComponent(BindingGroup bindingGroup, Object source, String property) {

        String sourceProperty = property + '.' + getFieldName();

        // Propriedade do source para bind deste objeto.
        Application.get().getBindingBuilder().createBinding(bindingGroup,
                source, sourceProperty, this, PROP_VALUE,
                null, false, false);
    }

    /**
     * Retorna o Componente de edição da seleção.
     * 
     * @return um FieldInput entre os fields
     */
    private FieldInput getFieldInput(){
        for (FieldComponent fc : fields) {
            if(fc instanceof FieldInput){
                return (FieldInput) fc;
            }
        }
        throw new AssertionError(this);
    }

    /**
     * Retorna um Criterion.
     * @param prefix Prefixo para ser utilizado como preNames na construção do
     * Criterion.
     * @return Retorna um Criterion.
     */
    public Criterion getCriterion() {

        if (criterion == null){
            try{
                criterion = Application.get()
                        .getFactory()
                        .createBaliCriterion(getFieldName());
                if(fieldPreName != null && fieldPreName.length() > 0){
                    if(fieldPreName.contains("."))
                        for (String s : fieldPreName.split(".")) {
                            criterion.addPreName(s);
                        }
                    else
                        criterion.addPreName(fieldPreName);
                }
            }catch (FactoryException fex){
                System.out.println("Falha ao criar BaliCriterion durante" +
                        " SelectionPanelFiels.getCriterion:\n" + fex.getMessage());
            }
        }

        Object obj1 = null;

        try{

            FieldInput field = getFieldInput();
            obj1 = field.getComponentManager().readConvertedValue(
                        field.getVisualRepresentation(), false);
            
        }catch (ComponentConverterException ccex){
            System.out.println("Falha ao ler dados do componente durante" +
                    " SelectionPanelField.getCriterion:\n" + ccex.getMessage());
        }

        criterion.setFilterType(getFilterType());
        criterion.setEmptyForAll(false);
        criterion.setValue(obj1);

        return criterion;
    }

    /**
     * Não utilizado por CriterionablePanelField
     * @param emptyForAll
     */
    public void setEmptyForAll(boolean emptyForAll) {
        // nop
    }

    /**
     * Verifica se os valores do filtro foram modificados desde a última
     * chamada getCriterion().
     */
    public boolean isModified() {

        Object obj1 = null;

        try{

            FieldInput field = getFieldInput();
            obj1 = field.getComponentManager().readConvertedValue(
                        field.getVisualRepresentation(), false);
            
        }catch (ComponentConverterException ccex){
            System.out.println("Falha ao ler dados do componente durante" +
                    " SelectionPanelField.isModified:\n" + ccex.getMessage());
        }

        return (!(value == null && obj1 == null) &&
                !(value != null && value.equals(obj1)));
    }

    @Override
    public boolean setEnabled(boolean value) {
        return false;
    }
}
